#include "che_ply.h"

#include <fstream>
#include <vector>
#include <cstring>
#include <cassert>
#include <map>

using namespace std;


// geometry processing and shape analysis framework
namespace gproshan {


che_ply::che_ply(const string & file)
{
	init(file);
}

che_ply::che_ply(const che_ply & mesh): che(mesh)
{
}

void che_ply::read_file(const string & file)
{
	map<string, size_t> bytes =	{
									{"char", 1},
									{"uchar", 1},
									{"short", 2},
									{"ushort", 2},
									{"int", 4},
									{"uint", 4},
									{"float", 4},
									{"float32", 4},
									{"float64", 8},
									{"double", 8}
								};

	size_t n_v = 0, n_f = 0, b;

	string str, format, element;
	
	ifstream is(file);
	assert(is.good());
	
	size_t xyz = 0, vbytes = 0;
	size_t fn = 0, fbytes = 0;

	while(getline(is, str) && str != "end_header")
	{
		stringstream ss(str);

		ss >> str;
		if(str == "element")
		{
			ss >> element;
			if(element == "vertex") ss >> n_v;
			if(element == "face") ss >> n_f;
		}
		
		if(str == "property" && element == "vertex")
		{
			ss >> str;
			vbytes += b = bytes[str];

			ss >> str;
			if(str == "x" || str == "y" || str == "z")
				xyz = b;
		}
		
		if(str == "property" && element == "face")
		{
			ss >> str;
			if(str == "list")
			{
				ss >> str; fn = bytes[str];
				ss >> str; fbytes = bytes[str];
			}
		}

		if(str == "format") ss >> format;
	}
	
	init(n_v, n_f);
	
	if(format == "ascii")
	{
		for(index_t v = 0; v < n_vertices_; v++)
		{
			getline(is, str);
			stringstream ss(str);
		
			ss >> GT[v];
		}
		
		index_t p, he = 0;
		while(n_f--)
		{
			getline(is, str);
			stringstream ss(str);
			
			ss >> p;
			while(p--)
				ss >> VT[he++];
		}
	}
	else // binary_little_endian
	{
		char vbuffer[vbytes];
		for(index_t v = 0; v < n_vertices_; v++)
		{
			is.read(vbuffer, vbytes);

			if(xyz == sizeof(real_t))
				memcpy(&GT[v], vbuffer, 3 * sizeof(real_t));
			else
			{
				if(xyz == 4)
				{
					float * X = (float *) vbuffer;
					for(index_t i = 0; i < 3; i++)
						GT[v][i] = X[i];
				}
				else
				{
					double * X = (double *) vbuffer;
					for(index_t i = 0; i < 3; i++)
						GT[v][i] = (real_t) X[i];
				}
			}
		}

		//char fbuffer[fbytes];	// preparing for a hexagon
		index_t p, he = 0;
		while(n_f--)
		{
			fn = 0;
			is.read(vbuffer, fn);

			if(fn == 1) p = *((char *) vbuffer);
			if(fn == 2) p = *((short *) vbuffer);
			if(fn == 4) p = *((int *) vbuffer);
			
			while(p--)
			{
				is.read(vbuffer, fbytes);
				if(fbytes == 1) VT[he++] = *((char *) vbuffer);
				if(fbytes == 2) VT[he++] = *((short *) vbuffer);
				if(fbytes == 4) VT[he++] = *((int *) vbuffer);
			}
		}
	}

	is.close();
}

void che_ply::write_file(const che * mesh, const string & file)
{
	ofstream os(file + ".ply");
	
	os << "ply" << endl;
	os << "format ascii 1.0" << endl;
	os << "comment generated by gproshan 2019" << endl;
	os << "element vertex " << mesh->n_vertices() << endl;
	os << "property float x" << endl;
	os << "property float y" << endl;
	os << "property float z" << endl;
	os << "element face " << mesh->n_faces() << endl;
	os << "property list uchar int vertex_index" << endl;
	os << "end_header" << endl;

	for(size_t v = 0; v < mesh->n_vertices(); v++)
		os << mesh->gt(v) << endl;

	for(index_t he = 0; he < mesh->n_half_edges(); )
	{
		os << che::P;
		for(index_t i = 0; i < che::P; i++)
			os << " " << mesh->vt(he++);
		os << endl;
	}

	os.close();
}


} // namespace gproshan

